<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
    <title>时钟</title>
</head>
<body>
<div style="margin: 50px">
    <canvas width="300" height="300"></canvas>
    <canvas width="200" height="200" style="background-color: #bbbbbb"></canvas>
    <canvas width="200" height="200"></canvas>
    <canvas width="200" height="200"></canvas>
</div>

<script>

    var clockHelper = function (canvas, config) {
        if (!config) {
            config = {}
        }
        var ctx = canvas.getContext('2d');
        var deColor = config.deColor ? config.deColor : '#333333';
        var deConfig = {
            ringWidth: config.ringWidth ? config.ringWidth : 6,//圆环宽度
            ringColor: config.ringColor ? config.ringColor : deColor,//圆环颜色
            hSaleL: config.hSaleL ? config.hSaleL : 8,//时刻度线长
            hScaleWidth: config.hScaleWidth ? config.hScaleWidth : 4,//时刻度线宽
            hScaleColor: config.hScaleColor ? config.hScaleColor : deColor,//时刻度颜色
            msSaleL: config.msSaleL ? config.msSaleL : 4,//分秒刻度线长
            msScaleWidth: config.msScaleWidth ? config.msScaleWidth : 2,//分秒刻度线宽
            msScaleColor: deColor,//分秒刻度颜色
            hFontSize: config.hFontSize ? config.hFontSize : 18,//整点文字大小
            hHandWidth: config.hHandWidth ? config.hHandWidth : 10,//时针宽度
            mHandWidth: config.mHandWidth ? config.mHandWidth : 5,//分针宽度
            sHandWidth: config.sHandWidth ? config.sHandWidth : 2,//秒针宽度

            hHandColor: config.hHandColor ? config.hHandColor : deColor,//时针颜色
            mHandColor: config.mHandColor ? config.mHandColor : deColor,//分针颜色
            sHandColor: config.sHandColor ? config.sHandColor : '#bb3333',//秒针颜色
            handMode: ['ms', 's'].indexOf("" + config.handMode) !== -1 ? config.handMode : 's',//指针读秒模式，ms:毫秒，s：秒。
            clockFaceColor: config.clockFaceColor ? config.clockFaceColor : '',
        };
        var ox = canvas.width / 2;
        var oy = canvas.height / 2;
        var width = canvas.width;
        var height = canvas.height;

        ctx.font = deConfig.hFontSize + "px 黑体";
        //中线圆环半径
        var ringR = (width < height) ? (width / 2 - deConfig.ringWidth / 2) : (height / 2 - deConfig.ringWidth / 2);
        //内圆环半径
        var ringInnerR = (width < height) ? (width / 2 - deConfig.ringWidth) : (height / 2 - deConfig.ringWidth);

        var timer;
        var timeSleep = 100;
        var isStart = false;

        function start() {
            if (isStart) {
                return;
            }
            isStart = true;

            if (deConfig.handMode == 'ms') {
                timeSleep = 100;
            } else {
                timeSleep = 1000;
            }

            ctx.clearRect(0, 0, width, height);
            draw();

            timer = setInterval(function () {
                if (isStart) {
                    ctx.clearRect(0, 0, width, height);
                    draw();
                }
            }, timeSleep);

        }

        function stop() {
            isStart = false;
            clearInterval(timer)
        }

        function draw() {

            beforeDraw();

            drawCircleFace();
            drawHands();

            afterDraw();

        }

        function drawCircleFace() {

            ctx.fillStyle = deConfig.ringColor;
            ctx.strokeStyle = deConfig.ringColor;

            ctx.lineWidth = deConfig.ringWidth;
            ctx.beginPath();
            ctx.arc(ox, oy, ringR, 0, Math.PI * 2);
            ctx.stroke();

            if (deConfig.clockFaceColor) {
                ctx.fillStyle = deConfig.clockFaceColor;
                ctx.fill();
            }


            var x1 = ox;
            var y1 = oy;
            var x2 = ox;
            var y2 = oy;
            var radin = 0;
            ctx.lineWidth = deConfig.hScaleWidth;

            // ctx.beginPath();
            for (var i = 1; i <= 60; i++) {
                radin = i * 6 * Math.PI / 180;
                x1 = ox + ringInnerR * Math.sin(radin);
                y1 = oy - ringInnerR * Math.cos(radin);

                if (i % 5 === 0) {
                    ctx.lineWidth = deConfig.hScaleWidth;
                    x2 = ox + (ringInnerR - deConfig.hSaleL) * Math.sin(radin);
                    y2 = oy - (ringInnerR - deConfig.hSaleL) * Math.cos(radin);

                    ctx.fillStyle = deConfig.hScaleColor;
                    var numText = i / 5 + "";
                    var textWidth = ctx.measureText(numText).width;


                    var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin);
                    var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin);
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'middle';

                    //不设置文字居中，基线居中，自己计算。貌似都有误差。因为旋转过程中，角度变化，且文字宽高不尽相同
                    // var x3 = ox + (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.sin(radin) - textWidth / 2;
                    // var y3 = oy - (ringInnerR - deConfig.hSaleL - deConfig.hFontSize) * Math.cos(radin) + deConfig.hFontSize/ 2;
                    //x2，y2已经求过，化简为：
                    // var x3 = x2 - deConfig.hFontSize * Math.sin(radin) - textWidth / 2;
                    // var y3 = y2 + deConfig.hFontSize * Math.cos(radin) + textWidth / 2;
                    //文字x轴向左偏移一半文字宽，使之水平居中;向下偏移一半高度,使之垂直居中。
                    // 实际中发现，字高没法测（api无），而使用fontSize不准。但y轴加上字宽，位置倒是更对齐一些。

                    // var x3 = x2 + textWidth / 2;
                    // var y3 = y2 - deConfig.hFontSize / 2;

                    ctx.fillText(numText, x3, y3);

                } else {
                    ctx.lineWidth = deConfig.msScaleWidth;
                    x2 = ox + (ringInnerR - deConfig.msSaleL) * Math.sin(radin);
                    y2 = oy - (ringInnerR - deConfig.msSaleL) * Math.cos(radin);
                }


                ctx.beginPath();
                ctx.moveTo(x1, y1);
                ctx.lineTo(x2, y2);
                ctx.stroke();

            }

        }

        //改变坐标中点，并旋转画布也许是更好的选择。
        function drawHands() {
            var date = new Date();

            var h = date.getHours() % 12;

            var m = date.getMinutes();
            var s = date.getSeconds();
            var ms = date.getMilliseconds();
            // console.log(h + "：" + m + "：" + s);
            //    时针

            var hRadin = (h + m / 60 + s / 3600) * Math.PI * 2 / 12;
            var mRadin = (m + s / 60) * Math.PI * 2 / 60;

            var sRadin;
            if (deConfig.handMode == 'ms') {
                sRadin = (s + ms / 1000) * Math.PI * 2 / 60;
            } else {
                sRadin = s * Math.PI * 2 / 60;
            }

            var x = 0;
            var y = 0;

            var hDotR = deConfig.hHandWidth + 2;
            var mDotR = 0.6 * hDotR
            var sDotR = 0.5 * hDotR

            //秒针半径
            var sHandR = ringInnerR - deConfig.hSaleL * 2
            //分针半径
            var mHandR = 0.8 * sHandR;
            //时针半径
            var hHandR = 0.7 * mHandR;

            //时针
            ctx.beginPath();
            ctx.lineWidth = deConfig.hHandWidth;
            ctx.strokeStyle = deConfig.hHandColor;
            ctx.strokeStyle = deConfig.hHandColor;
            ctx.moveTo(ox, oy);
            x = ox + hHandR * Math.cos(hRadin - Math.PI / 2);
            y = oy + hHandR * Math.sin(hRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();

            //针尖。直接圆型了（矩形指针来绘制针尖，计算复杂。。。）
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.hHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();

            //中心
            ctx.beginPath();
            // ctx.lineWidth = hDotR;
            ctx.arc(ox, oy, hDotR / 2, 0, Math.PI * 2);
            ctx.fill();
            ctx.stroke();

            //分针
            ctx.beginPath();
            ctx.lineWidth = deConfig.mHandWidth;
            ctx.strokeStyle = deConfig.mHandColor;
            ctx.fillStyle = deConfig.mHandColor;
            ctx.moveTo(ox, oy);
            x = ox + mHandR * Math.cos(mRadin - Math.PI / 2);
            y = oy + mHandR * Math.sin(mRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();

            //针尖。直接圆型了（矩形指针来绘制针尖，计算复杂。。。）
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.mHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();

            //中心
            ctx.beginPath();
            ctx.arc(ox, oy, mDotR / 2, 0, Math.PI * 2);
            ctx.stroke();

            //秒针
            ctx.beginPath();
            ctx.strokeStyle = deConfig.sHandColor;
            ctx.fillStyle = deConfig.sHandColor;
            ctx.lineWidth = deConfig.sHandWidth;
            //秒针有长短两线
            x = ox - sHandR / 4 * Math.cos(sRadin - Math.PI / 2);
            y = oy - sHandR / 4 * Math.sin(sRadin - Math.PI / 2);
            ctx.moveTo(x, y);
            x = ox + sHandR * Math.cos(sRadin - Math.PI / 2);
            y = oy + sHandR * Math.sin(sRadin - Math.PI / 2);
            ctx.lineTo(x, y);
            ctx.stroke();

            //针尖。直接圆型了（矩形指针来绘制针尖，计算复杂。。。）
            ctx.beginPath();
            ctx.lineWidth = 0;
            ctx.arc(x, y, deConfig.sHandWidth / 2, 0, 2 * Math.PI);
            ctx.fill();

            //中心
            ctx.beginPath();
            ctx.fillStyle = deColor;
            ctx.arc(ox, oy, sDotR, 0, Math.PI * 2);
            ctx.fill();
            ctx.stroke();

        }

        function beforeDraw() {
            if (typeof exp.beforeDraw === 'function') {
                exp.beforeDraw(ctx, deConfig);
            }
        }

        function afterDraw() {
            if (typeof exp.afterDraw === 'function') {
                exp.afterDraw(ctx, deConfig);
            }
        }

        var exp = {
            start: start,
            stop: stop,
            beforeDraw: null,
            afterDraw: null,
        }
        return exp;


    }

    var clockCanvas1 = document.getElementsByTagName('canvas')[0];
    var clockCanvas2 = document.getElementsByTagName('canvas')[1];
    var clockCanvas3 = document.getElementsByTagName('canvas')[2];
    var clockCanvas4 = document.getElementsByTagName('canvas')[3];


    var clock = clockHelper(clockCanvas1, {mHandColor: '#3333bb', sHandColor: '#bb3333'});
    clock.start();
    setTimeout(function () {
        clock.stop()
    }, 5000)
    setTimeout(function () {
        clock.start();
    }, 8000)

    clockHelper(clockCanvas2, {
        mHandColor: 'green',
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: 'ms'
    }).start();


    clockHelper(clockCanvas2, {
        mHandColor: 'green',
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: 'ms'
    }).start();


    clockHelper(clockCanvas3, {
        deColor: '#bbbbbb',
        sHandColor: '#bbbbbb',
        clockFaceColor: '#112233',//钟面
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: 's'
    }).start();

    var clock4 = clockHelper(clockCanvas4, {
        deColor: '#bbbbbb',
        sHandColor: '#bbbbbb',
        // clockFaceColor: '#112233',
        hHandWidth: 6,
        mHandWidth: 4,
        hFontSize: 14,
        hScaleWidth: 2,
        handMode: 's'
    });

    clock4.afterDraw = function (ctx, deConfig) {
        var grd = ctx.createLinearGradient(0, 0, clockCanvas4.width, clockCanvas4.height);
        grd.addColorStop(0, "rgba(255,0,0,0.3)");
        grd.addColorStop(1, "rgba(0,0,255,0.5)");
        ctx.fillStyle = grd;
        ctx.arc(clockCanvas4.width/2,clockCanvas4.height/2,clockCanvas4.width/2,0,Math.PI*2);
        // ctx.fillRect(0, 0, clockCanvas4.width, clockCanvas4.height);
        ctx.fill();

        ctx.fillText('时钟绘制完成后，自定义其他绘制',clockCanvas4.width/2,clockCanvas4.height - deConfig.hFontSize);
    };

    clock4.start();

</script>
</body>
</html>